#! /usr/bin/python2.4

# Copyright 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""check_gvn_revprops.py unittest"""


import unittest

import svn.fs

import gvn.hooks.info

import testing.hooks.common

# import RunHook to pass the function pointer to testcommon in unittests
from gvn.hooks.pre_revprop_change.check_gvn_revprops import RunHook

# Which revision we consider our head
HEADREV=4

class TestGvnRevPropsChange(testing.hooks.common.HookTestCase):

  def setUp(self):
    # HookTestCase creates and sets a _logger object based on whether
    # GVN_TEST_DEBUG is exported or not
    testing.hooks.common.HookTestCase.setUp(self)

    # create group pickle to use as our group file
    user_groups= {
      "polly": [ "group1" ],
      "adminuser": [ testing.hooks.common.SUPER_GROUP ],
    }

    for user in user_groups:
      self.userdb.WriteUserGroups(user, user_groups[user])

  def testPropSet(self):
    """A random user can set a random property
    """
    self.assertHookResult(self._ChangeProps(RunHook, "polly", "randomprop", 
                           "A"), 0)

  def testAdminUserPropSet(self):
    """An admin user can set a random property
    """
    self.assertHookResult(self._ChangeProps(RunHook, "adminuser", "randomprop",
                           "A"), 0)

  def testGvnPropSet(self):
    """A random user can set a gvn:approve:user property
    """
    self.assertHookResult(self._ChangeProps(RunHook, "usr1", "gvn:approve:usr1",
                           "A"), 0)

  def testGvnRandomPropSet(self):
    """A random user cannot set a gvn:x property
    """
    self.assertHookResult(self._ChangeProps(RunHook, "polly", "gvn:foo", "A"),
                "polly cannot add gvn:foo on revision 1")

  def testAdminUserGvnPropSet(self):
    """An admin user can set a gvn:x property
    """
    self.assertHookResult(self._ChangeProps(RunHook, "adminuser", "gvn:foo", 
                           "A"), "adminuser cannot add gvn:foo on revision 1")

  def testNormalUserBypassPropSet(self):
    """A normal user cannot set a gvn:bypass-hooks property
    """
    self.assertHookResult(self._ChangeProps(RunHook, "polly", 
       "gvn:bypass-hooks", "A"), 
       "polly cannot add gvn:bypass-hooks on revision 1")

  def testAdminUserBypassPropSet(self):
    """An admin user cannot set a gvn:bypass-hooks property
    """
    self.assertHookResult(self._ChangeProps(RunHook, "adminuser", 
       "gvn:bypass-hooks", "A"), 
       "adminuser cannot add gvn:bypass-hooks on revision 1")

  def testAdminUserBypassPropRemove(self):
    """An admin user cannot remove a gvn:bypass-hooks property
    """
    self.assertHookResult(self._ChangeProps(RunHook, "adminuser", 
           "gvn:bypass-hooks", "D"), 
           "adminuser cannot delete gvn:bypass-hooks on revision 1")

  def testNormalUserBypassPropRemove(self):
    """A user cannot touch a gvn:bypass-hooks property
    """
    self.assertHookResult(self._ChangeProps(RunHook, "polly", 
           "gvn:bypass-hooks", "M"),
            "polly cannot modify gvn:bypass-hooks on revision 1")

  def testGvnRemoveSubmitted(self):
    """A mortal user cannot remove gvn:submitted
    """
    self.assertHookResult(
        self._ChangeProps(RunHook, "polly", "gvn:submitted", "D", "4"),
        "Error: user polly is not allowed to delete property gvn:submitted")

  def testAdminUserGvnRemoveSubmitted(self):
    """A superuser can remove gvn:submitted
    """
    self.assertHookResult(
        self._ChangeProps(RunHook, "adminuser", "gvn:submitted", "D", "4"),
        "Error: user adminuser is not allowed to delete property gvn:submitted")

  def testGvnModifySubmitted(self):
    """A mortal user cannot modify gvn:submitted
    """
    self.assertHookResult(
        self._ChangeProps(RunHook, "polly", "gvn:submitted", "M", "4"),
        "Error: user polly is not allowed to modify property gvn:submitted")

  def testAdminUserGvnModifySubmitted(self):
    """A superuser can modify gvn:submitted
    """
    self.assertHookResult(
        self._ChangeProps(RunHook, "adminuser", "gvn:submitted", "M", "4"),
        "Error: user adminuser is not allowed to modify property gvn:submitted")

  def assertSubmitted(self, expected_result,
                      submitted_rev, submitted,
                      change_rev=None, change=None):
    """Assert that a gvn:change/gvn:submitted pair return expected_result.

    Mock svn.fs.revision_prop so that when called on submitted_rev or
    change_rev it returns submitted or change, respectively.  Then, call
    self._ChangeProps to test Adding gvn:submitted (using submitted as the
    value) on submitted_rev.  Use self.assertHookResult to assert that the
    result from self._ChangeProps matches expected_result.

    Arguments:
    submitted_rev       -- revision on which to set gvn:submitted
    submitted           -- value to which to set gvn:submitted
    change_rev          -- revision on which to set gvn:change (default None)
    change              -- value to which to set gvn:change (default None)
    """
    def _FakeRevProp(fs, rev, propname, pool):
      if rev not in [change_rev, submitted_rev]:
        self.fail("%d == neither %s nor %s" % (rev, change_rev, submitted_rev))
      if rev == change_rev:
        self.assertEqual(propname, 'gvn:change')
        return change
      if rev == submitted_rev:
        self.assertEqual(propname, 'gvn:submitted')
        return submitted
      self.fail("we don't know what's going on")
    orig = svn.fs.revision_prop
    svn.fs.revision_prop = _FakeRevProp
    try:
      self.assertHookResult(self._ChangeProps(RunHook, "polly", "gvn:submitted",
             "A", head=HEADREV, rev=submitted_rev, prop_value=submitted), 
             expected_result)
    finally:
      svn.fs.revision_prop = orig

  def testAddGvnSubmitted(self):
    """Make sure the gvn:submitted logic works."""
    # set gvn:submitted='' on r1 => BLOCK
    self.assertSubmitted('polly tried to set revprop gvn:submitted to invalid ',
                         submitted_rev=1, submitted='')
    # set gvn:submitted='q' on r1 => BLOCK
    self.assertSubmitted('polly tried to set revprop gvn:submitted to invalid',
                         submitted_rev=1, submitted='q')
    # set gvn:submitted='-1' on r1 => BLOCK
    self.assertSubmitted("-1 is an invalid revision number for gvn:submitted",
                         submitted_rev=1, submitted='-1')
    # set gvn:submitted='0' => BLOCK
    self.assertSubmitted("0 is an invalid revision number for gvn:submitted",
                         submitted_rev=1, submitted='0')
    # set gvn:submitted=too high a revision => BLOCK
    self.assertSubmitted(("%d is an invalid revision number for gvn:submitted"
                          % (HEADREV * 1000,)),
                         submitted_rev=1, submitted=str(HEADREV * 1000))
    # set gvn:submitted='2' on r1 => BLOCK
    self.assertSubmitted("Cannot set gvn:submitted to r2, which has no gvn:change",
                         change_rev=2, change=None,
                         submitted_rev=1, submitted='2')
    # 1. set gvn:change='' on r2
    # 2. set gvn:submitted='2' on r1 => BLOCK
    self.assertSubmitted("Cannot set gvn:submitted to r2, whose gvn:change () is invalid",
                         change_rev=2, change='',
                         submitted_rev=1, submitted='2')
    # 1. set gvn:change='1' on r2
    # 2. set gvn:submitted='2' on r1 => BLOCK
    self.assertSubmitted("Cannot set gvn:submitted to r2, whose gvn:change (1) is invalid",
                         change_rev=2, change='1',
                         submitted_rev=1, submitted='2')
    # 1. set gvn:change='manuel' on r2
    # 2. set gvn:submitted='2' on r1 => BLOCK
    self.assertSubmitted("Cannot set gvn:submitted to r2, whose gvn:change (manuel) is invalid",
                         change_rev=2, change='manuel',
                         submitted_rev=1, submitted='2')
    # 1. set gvn:change='manuel@1' on r2
    # 2. set gvn:submitted='2' on r1 => BLOCK
    self.assertSubmitted("Cannot set gvn:submitted to r2, whose gvn:change (manuel@1) is invalid",
                         change_rev=2, change='manuel@1',
                         submitted_rev=1, submitted='2')
    # 1. set gvn:change='manuel/foo' on r2
    # 2. set gvn:submitted='2' on r1 => BLOCK
    self.assertSubmitted("Cannot set gvn:submitted to r2, whose gvn:change (manuel/foo) is invalid",
                         change_rev=2, change='manuel/foo',
                         submitted_rev=1, submitted='2')
    # 1. set gvn:change='manuel/foo@' on r2
    # 2. set gvn:submitted='2' on r1 => BLOCK
    self.assertSubmitted("Cannot set gvn:submitted to r2, whose gvn:change (manuel/foo@) is invalid",
                         change_rev=2, change='manuel/foo@',
                         submitted_rev=1, submitted='2')
    # 1. set gvn:change='manuel/foo@q' on r2
    # 2. set gvn:submitted='2' on r1 => BLOCK
    self.assertSubmitted("Cannot set gvn:submitted to r2, whose gvn:change (manuel/foo@q) is invalid",
                         change_rev=2, change='manuel/foo@q',
                         submitted_rev=1, submitted='2')
    # 1. set gvn:change='manuel/foo@-1' on r2
    # 2. set gvn:submitted='2' on r1 => BLOCK
    self.assertSubmitted("Cannot set gvn:submitted to r2, whose gvn:change (manuel/foo@-1) does not point to 1",
                         change_rev=2, change='manuel/foo@-1',
                         submitted_rev=1, submitted='2')
    # 1. set gvn:change='manuel/foo@0' on r2
    # 2. set gvn:submitted='2' on r1 => BLOCK
    self.assertSubmitted("Cannot set gvn:submitted to r2, whose gvn:change (manuel/foo@0) does not point to 1",
                         change_rev=2, change='manuel/foo@0',
                         submitted_rev=1, submitted='2')
    # 1. set gvn:change='manuel/foo@5' on r2
    # 2. set gvn:submitted='2' on r1 => BLOCK
    # (because 5 != 2)
    self.assertSubmitted("Cannot set gvn:submitted to r2, whose gvn:change (manuel/foo@5) does not point to 1",
                         change_rev=2, change='manuel/foo@5',
                         submitted_rev=1, submitted='2')
    # 1. set gvn:change='basil/foo@1' on r2
    # 2. set gvn:submitted='2' on r1 => BLOCK
    self.assertSubmitted("Cannot set gvn:submitted on basil's change (basil/foo@1)",
                         change_rev=2, change='basil/foo@1',
                         submitted_rev=1, submitted='2')
    # 1. snapshot polly/foo@1
    # 2. submit as rev 4, setting gvn:change=polly/foo@1 on r4
    # 3. set gvn:submitted=4 on r1 => PASS
    self.assertSubmitted(0,
                         change_rev=4, change='polly/foo@1',
                         submitted_rev=1, submitted='4')
